﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows.Forms;
using AsmDependency.Core.AsmApplication;
using AsmDependency.Core.UserInterface;
using AsmDependency.UIComponents;
using AsmDependency.Utility;

namespace AsmDependency
{
    internal enum StartupAction
    {
        NewFile,
        OpenFile,
        None
    }

    public partial class ApplicationWindow : CApplicationWindow
    {
        #region [=== VARIABLES ===]
        bool _processing = false;
        string _DomainDirectory = string.Empty;
        string _componentPath = string.Empty;
        string _componentConfigPath = string.Empty;
        string _targetComponentDirectory = string.Empty;

        DependencyAnalizerAsyncTask _dependencyAnalizer;

        #endregion

        public ApplicationWindow(IServiceProvider _serviceProvider)
            : base(_serviceProvider)
        {
            InitializeComponent();
            IServiceContainer service = (IServiceContainer)this.GetService(typeof(IServiceContainer));
            //service.AddService(typeof(IWindowManager), this._windowManager);
            this._gradientBand.Stop();
        }

        #region [=== OVERRIDES ===]
        protected override bool PerformStartupAction()
        {
            ICommandManager service = (ICommandManager)this.GetService(typeof(ICommandManager));
            service.UpdateCommands(true);
            IAsmApplicationIdentity applicationIdentity = (IAsmApplicationIdentity)base.ServiceProvider.GetService(typeof(IAsmApplicationIdentity));
            CCommandLine commandLine = applicationIdentity.CommandLine;
            if ((int)commandLine.Arguments.Length <= 0)
            {
                string setting = applicationIdentity.GetSetting("startup");
                if (setting == null)
                {
                    setting = string.Empty;
                }
                int num = -1;
                if (setting.Length == 0 || setting == Enum.GetName(typeof(StartupAction), StartupAction.NewFile))
                {
                    num = 1;
                }
                else
                {
                    if (setting == Enum.GetName(typeof(StartupAction), StartupAction.OpenFile))
                    {
                        num = 2;
                    }
                }
                if (num == -1)
                {
                    return false;
                }
                else
                {
                    service.InvokeCommand(typeof(GlobalCommands), num);
                    return true;
                }
            }
            else
            {
                base.OpenDocuments(commandLine.Arguments);
                return true;
            }
        }

        protected override bool UpdateCommand(Command command)
        {
            bool flag = false;
            if (command.CommandGroup == typeof(GlobalCommands))
            {
                int commandID = command.CommandID;
                if (commandID != 5)
                {
                    if (commandID == 214)
                    {
                        command.Checked = true; // this._leftContainer.get_Visible();
                        flag = true;
                        if (!flag)
                        {
                            flag = base.UpdateCommand(command);
                        }
                        return flag;
                    }
                    else
                    {
                        if (commandID == 400 || commandID == 401 || commandID == 402 || commandID == 403)
                        {
                            goto Label1;
                        }
                        if (!flag)
                        {
                            flag = base.UpdateCommand(command);
                        }
                        return flag;
                    }
                }
            Label1:
                command.Enabled = (int)base.MdiChildren.Length != 0;
                flag = true;
            }
            if (!flag)
            {
                flag = base.UpdateCommand(command);
            }
            return flag;
        }

        protected override bool HandleCommand(Command command)
        {
            bool flag = false;
            bool flag1 = false;
            if (command.CommandGroup == typeof(GlobalCommands))
            {
                int commandID = command.CommandID;
                if (commandID == 5) {
                    base.ActiveMdiChild.Close();
                    flag = true;
                    flag1 = true;
                } else {
                    if (commandID == 214) {
                        //this.OnCommandViewToolbox();
                        flag = true;
                        flag1 = true;
                    } else {
                        switch (commandID)
                        {
                            case 400:
                                {
                                    base.LayoutMdi(0);
                                    flag = true;
                                    break;
                                }
                            case 401:
                                {
                                    //base.LayoutMdi(1);
                                    flag = true;
                                    break;
                                }
                            case 402:
                                {
                                    //base.LayoutMdi(2);
                                    flag = true;
                                    break;
                                }
                            case 403:
                                {
                                    Form[] mdiChildren = base.MdiChildren;
                                    if (mdiChildren != null && (int)mdiChildren.Length != 0) {
                                        for (int i = (int)mdiChildren.Length - 1; i >= 0; i--) {
                                            mdiChildren[i].Close();
                                        }
                                    }
                                    flag = true;
                                    break;
                                }
                        }
                    }
                }
            }
            if (flag) {
                if (flag1) {
                    ICommandManager service = (ICommandManager)this.GetService(typeof(ICommandManager));
                    service.UpdateCommands(false);
                }
            } else {
                flag = base.HandleCommand(command);
            }
            return flag;
        }

        protected override void OnInitialActivated(EventArgs e)
        {
            //SplashScreen.Current.Hide(this);
            base.OnInitialActivated(e);
        }
        #endregion

        #region [=== PROPERTIES ===]
        private bool Processing
        {
            get { return this._processing; }
            set {
                if (this._processing != value) {
                    this._processing = value;
                    if (this._processing) {
                        this.AnalyzeAssbemblyButton.Text = "Cancelar Analizar y Recolectar";
                        this.browseButton.Enabled = false;
                        this.targetDirectoryButton.Enabled = false;
                        this._gradientBand.Start();
                    } else {
                        this.AnalyzeAssbemblyButton.Text = "Analizar y Recolectar Referencias";
                        this.browseButton.Enabled = true;
                        this.targetDirectoryButton.Enabled = true;
                        this._gradientBand.Stop();
                    }
                }
            }
        }
        #endregion

        #region [=== EVENT HANDLERS ===]
        private void browseButton_Click(object sender, EventArgs e)
        {
            OpenFileDialog _dialog = new OpenFileDialog();
            _dialog.Filter = "";
            _dialog.Title = "Select Component";
            _dialog.CheckFileExists = true;
            _dialog.DereferenceLinks = true;
            _dialog.Multiselect = false;
            _dialog.Filter = "Components (*.dll,*.exe,*.mcl)|*.dll;*.exe;*.mcl|All files (*.*)|*.*";

            if (_dialog.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    _componentPath = _dialog.FileName;
                    AssemblyPathTexBox.Text = _componentPath;
                    AssembliesNeeded.Items.Clear();
                    FileInfo _componentInfo = new FileInfo(_componentPath);
                    _DomainDirectory = _componentInfo.Directory.FullName;
                    _componentConfigPath = string.Format("{0}{1}", _componentInfo.FullName, ".config");
                } catch (Exception) {
                    //((IUIService) this.GetService(typeof(IUIService))).ShowError(exception, "Unable to load file.");
                }
            }
        }

        private void targetDirectoryButton_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog _dialog = new FolderBrowserDialog();
            if (_dialog.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    _targetComponentDirectory = _dialog.SelectedPath;
                    targetDirectoryPathTextBox.Text = _targetComponentDirectory;
                    AssembliesNeeded.Items.Clear();
                } catch (Exception) {
                    //((IUIService) this.GetService(typeof(IUIService))).ShowError(exception, "Unable to load file.");
                }
            }
        }

        private void AnalyzeAssbemblyButton_Click(object sender, EventArgs e)
        {
            try
            {
                if (this.Processing) {
                    this.Processing = false;
                } else {
                    this._dependencyAnalizer = new DependencyAnalizerAsyncTask(this, _DomainDirectory, _componentPath, _componentConfigPath, _targetComponentDirectory);
                    this._dependencyAnalizer.Start(new AsyncTaskResultPostedEventHandler(this.OnProcessingLibraryComplete));
                    this.Processing = true;

                    AssembliesNeeded.Items.Clear();
                }

            } catch (Exception) {
                //((IUIService) this.GetService(typeof(IUIService))).ShowError(exception, "Unable to load file.");
            }
        }

        internal void OnProcessingLibraryComplete(object sender, CAsyncTaskResultPostedEventArgs args)
        {
            AssemblyName[] _references = (AssemblyName[])args.Data;
            if (_references == null) {
                MessageBox.Show(this, "Error recolectando la información.", "Recolector de Referencias V 0.0.1", MessageBoxButtons.OK, MessageBoxIcon.Hand);
            } else {
                this.Processing = false;
                foreach (var item in _references) {
                    ListViewItem _listItem;
                    try
                    {
                        _listItem = new ListViewItem(new string[] { item.Name, item.Version.ToString(), Assembly.Load(item).ImageRuntimeVersion + " o Posterior", item.FullName });
                    } catch (Exception) {
                        _listItem = new ListViewItem(new string[] { item.Name, item.Version.ToString(), "No determinado...", item.FullName });
                    }
                    
                    AssembliesNeeded.Items.Add(_listItem);
                }
            }

        }
        #endregion

        internal class DependencyAnalizerAsyncTask : CAsyncTask
        {
            #region [=== VARIABLES ===]
            bool _processing = false;
            string _DomainDirectory = string.Empty;
            string _componentPath = string.Empty;
            string _componentConfigPath = string.Empty;
            string _targetComponentDirectory = string.Empty;

            DependencyAnalizerAsyncTask _dependencyAnalizer;

            AppDomainSetup _domainInformation;
            AppDomain _applicationDomain;
            ApplicationWindow _owner;
            AssemblyName[] _references;
            #endregion

            public DependencyAnalizerAsyncTask(ApplicationWindow owner, string domainDorectoy, string componentPath, string componentConfigPath, string targetComponentDirectory)
            {
                this._owner = owner;
                this._DomainDirectory = domainDorectoy;
                this._componentPath = componentPath;
                this._componentConfigPath = componentConfigPath;
                this._targetComponentDirectory = targetComponentDirectory;
            }

            #region [=== METHODS ===]
            internal string GetFromResources(string resourceName)
            {
                Assembly assem = Assembly.GetExecutingAssembly();
                using (Stream stream = assem.GetManifestResourceStream(resourceName))
                {
                    using (var reader = new StreamReader(stream))
                    {
                        return reader.ReadToEnd();
                    }
                }
            }
            List<AssemblyName> _GlobalAssemblyNameList = new List<AssemblyName>();
            internal AssemblyName[] GetReferences(string _asm)
            {
                // Determinate reference list
                Assembly asm = Assembly.LoadFile(_asm);
                List<AssemblyName> _AssemblyNameList = new List<AssemblyName>();
                List<AssemblyName> _AlternateAssemblyNameList = new List<AssemblyName>();
                foreach (AssemblyName item in asm.GetReferencedAssemblies())
                {
                    if (!GetFromResources("AsmDependency.BCL.txt").Contains(item.Name))
                    {
                        Console.WriteLine(item.Name);
                        Console.WriteLine(Assembly.GetAssembly(item.GetType()).CodeBase);
                        _AssemblyNameList.Add(item);
                    }

                }
                
                _GlobalAssemblyNameList.AddRange(_AssemblyNameList.ToArray());

                foreach (AssemblyName item in _AssemblyNameList)
                {
                    Assembly _Assembly;
                    string _comp = Path.Combine(_targetComponentDirectory, item.Name + ".dll");
                    if (File.Exists(Path.Combine(_DomainDirectory, item.Name + ".dll")))
                    {
                        _Assembly = Assembly.LoadFile(Path.Combine(_DomainDirectory, item.Name + ".dll"));
                        File.Copy(Path.Combine(_DomainDirectory, item.Name + ".dll"), Path.Combine(_targetComponentDirectory, item.Name + ".dll"), true);

                    }
                    else
                    {
                        _Assembly = Assembly.Load(item);
                        File.Copy(_Assembly.Location, Path.Combine(_targetComponentDirectory, item.Name + ".dll"), true);
                    }

                    Console.WriteLine(_Assembly.Location);
                    GetReferences(_Assembly.Location);
                    //try
                    //{
                    //    AssemblyName[] _list = GetReferences(_Assembly.Location);
                    //    if (_list.Length > 0) _AlternateAssemblyNameList.AddRange(_list);
                    //}
                    //catch (Exception e)
                    //{
                    //    Console.WriteLine(e.Message);
                    //}

                }
                //if (_AlternateAssemblyNameList.ToArray().Length > 0) {
                //    foreach (var item in _AlternateAssemblyNameList) {
                //        AssemblyName _as = _AssemblyNameList.Find(new Predicate<AssemblyName>(AsmSearch));
                //        if (_as != null)
                //            _AssemblyNameList.Add(item);
                //    }
                //}
                if(_AlternateAssemblyNameList.ToArray().Length > 0)_AssemblyNameList.AddRange(_AlternateAssemblyNameList.ToArray());
                return _AssemblyNameList.ToArray();
            }

            internal bool AsmSearch(AssemblyName asm)
            {
                bool S_OK = false;
                foreach (var item in _GlobalAssemblyNameList) {
                    if (asm.Name == item.Name)
                        S_OK = true;
                }

                return S_OK;
            }

            internal AssemblyName[] GetDistinct()
            {
                List<AssemblyName> _return = new List<AssemblyName>();
                foreach (var item in _GlobalAssemblyNameList)
                {
                    if (_return.ToArray().Length == 0)
                        _return.Add(item);
                    else
                    {
                        bool _addItem = true;
                        foreach (var returnItem in _return)
                        {
                            if (returnItem.Name.Equals(item.Name)) {
                                _addItem = false;
                                break;
                            }
                                
                        }
                        if (_addItem) _return.Add(item);
                    }
                }

                return _return.ToArray();
            }
            #endregion

            #region [=== OVERRIDE ===]
            protected override void PerformTask()
            {
                bool flag = false;
                try
                {
                    _domainInformation = new AppDomainSetup();
                    _domainInformation.ApplicationBase = _DomainDirectory;
                    _domainInformation.ConfigurationFile = _componentConfigPath;
                    _domainInformation.DisallowBindingRedirects = false;
                    _domainInformation.DisallowCodeDownload = true;

                    _applicationDomain = AppDomain.CreateDomain("MyDomain", null, _domainInformation);
                    AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(ApplicationDomain_AssemblyResolve);

                    _references = GetReferences(_componentPath);
                    _references = GetDistinct();
                    AppDomain.Unload(_applicationDomain);
                }
                catch
                {
                    flag = true;
                }
                finally
                {
                    //this._owner.Processing = false;
                }
                if (!flag)
                {
                    base.PostResults(_references, 100, true);
                }
                else
                {
                    base.PostResults(null, 100, true);
                }
            }
            #endregion

            #region [=== EVENTS HANDLERS ===]
            internal Assembly ApplicationDomain_AssemblyResolve(object sender, ResolveEventArgs args)
            {
                //This handler is called only when the common language runtime tries to bind to the assembly and fails.

                //Retrieve the list of referenced assemblies in an array of AssemblyName.
                Assembly MyAssembly, objExecutingAssemblies;
                string strTempAssmbPath = "";

                objExecutingAssemblies = Assembly.GetExecutingAssembly();
                AssemblyName[] arrReferencedAssmbNames = objExecutingAssemblies.GetReferencedAssemblies();

                //Loop through the array of referenced assembly names.
                foreach (AssemblyName strAssmbName in arrReferencedAssmbNames)
                {
                    //Check for the assembly names that have raised the "AssemblyResolve" event.
                    if (strAssmbName.FullName.Substring(0, strAssmbName.FullName.IndexOf(",")) == args.Name.Substring(0, args.Name.IndexOf(",")))
                    {
                        //Build the path of the assembly from where it has to be loaded.				
                        strTempAssmbPath = _DomainDirectory + args.Name.Substring(0, args.Name.IndexOf(",")) + ".dll";
                        break;
                    }

                }
                //Load the assembly from the specified path. 					
                MyAssembly = Assembly.LoadFrom(strTempAssmbPath);

                //Return the loaded assembly.
                return MyAssembly;
            }
            #endregion
        }
    }
}
